﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Chart2D
{
    /// <summary>
    /// Функция одной переменной.
    /// </summary>
    /// <param name="t">Независимый параметр.</param>
    /// <returns>Значение функции.</returns>
    delegate double Function(double t);

    class DoublePrecisionPoint
    {
        public double X { get; set; }
        public double Y { get; set; }

        public DoublePrecisionPoint()
        {
            X = Y = 0;
        }

        public DoublePrecisionPoint(double x, double y)
        {
            X = x;
            Y = y;
        }

        public double Module => Math.Sqrt(X * X + Y * Y);

        static public DoublePrecisionPoint operator -(DoublePrecisionPoint point1, DoublePrecisionPoint point2)
        {
            return new DoublePrecisionPoint { X = point1.X - point2.X, Y = point1.Y - point2.Y };
        }

        static public double operator *(DoublePrecisionPoint point1, DoublePrecisionPoint point2)
        {
            return point1.X * point2.X + point1.Y * point2.Y;
        }

        public override string ToString()
        {
            return string.Format("X = {0}, Y = {1}", X, Y);
        }
    }

    /// <summary>
    /// Класс для хранения данных односвязной кривой.
    /// </summary>
    class Curve
    {
        List<DoublePrecisionPoint> _points = new List<DoublePrecisionPoint>();

        public void Add(DoublePrecisionPoint point)
        {
            _points.Add(point);
        }

        public double MaxY => _points.Max(z => z.Y);
        public double MinY => _points.Min(z => z.Y);
        public double MaxX => _points.Max(z => z.X);
        public double MinX => _points.Min(z => z.X);
        public int Count => _points.Count();
        public DoublePrecisionPoint GetPoint(int index)
        {
            return _points[index];
        }
    }

    /// <summary>
    /// Класс для построения графиков функций.
    /// </summary>
    class Chart2D
    {
        #region === members ===

        List<Curve> _curves = null;

        #endregion

        #region === private ===

        DoublePrecisionPoint CreatePoint(double x, double y)
        {
            return (!IsPolar) ? new DoublePrecisionPoint(x, y) : new DoublePrecisionPoint(y * Math.Cos(x), y * Math.Sin(x));
        }

        /// <summary>
        /// Проверка функции на разрыв.
        /// </summary>
        /// <param name="y1">Первое значение функции.</param>
        /// <param name="y2">Второе значение функции.</param>
        /// <param name="h">Шаг аргумента.</param>
        /// <returns>True если есть подозрение на разрыв, False в противном случае.</returns>
        bool CheckInterrupt(double y1, double y2, double h)
        {
            bool result = false;

            // максимально допустимое значение прирощения по модулю.
            const double c_max_diff = 11.0;

            do
            {
                if (y1 == double.NaN || y2 == double.NaN)
                    break;
                
                if (Math.Abs((y1 - y2) / h) > c_max_diff)
                    break;

                result = true;

            } while (false);

            return result;
        }

        #endregion

        public bool IsPolar { get; set; } = false;
        public double MaxY => _curves.Max(c => c.MaxY);
        public double MinY => _curves.Min(c => c.MinY);
        public double MaxX => _curves.Max(c => c.MaxX);
        public double MinX => _curves.Min(c => c.MinX);

        public List<Curve> SetFunctions(Function functionX, Function functionY, double t1, double t2)
        {
            const int c_steps = 1024;

            _curves = new List<Curve>();
            Curve curve = null;
            double x, y, t;
            double kt = (t2 - t1) / (c_steps - 1);

            for (int i = 0; i < c_steps; i++)
            {
                t = kt * i + t1;
                x = functionX(t);
                y = functionY(t);

                if (CheckInterrupt(y, functionY(t - kt), kt))
                {
                    if (curve == null)
                        curve = new Curve();

                    curve.Add(CreatePoint(x, y));
                }
                else
                {
                    if (curve != null && curve.Count > 1)
                    {
                        _curves.Add(curve);
                        curve = null;
                    }
                }
            }

            if (curve != null && curve.Count > 1)
            {
                _curves.Add(curve);
            }

            return _curves;
        }
     }
}
